#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "libusb.h"
#include "types.h"
#include "remote.h"
#include "control.h"

#define RF_ERROR_SYNC	0xEE
#define RF_SETUP_SYNC	0xFC
#define RF_WRITE_SYNC	0xFD
#define RF_READ_SYNC		0xFE
#define RF_WRITE_OK		0x00
#define RF_WRITE_ERR		0xFF
#define RF_WRITE_IDLE    0xCC

/* USB request type values */
#define USBRQ_TYPE_MASK         0x60
#define USBRQ_TYPE_STANDARD     (0<<5)
#define USBRQ_TYPE_CLASS        (1<<5)
#define USBRQ_TYPE_VENDOR       (2<<5)

typedef union usbWord{
    unsigned short word;
    unsigned char  bytes[2];
}usbWord_t;

typedef struct usbRequest{
    unsigned char bmRequestType;
    unsigned char bRequest;
    usbWord_t   wValue;
    usbWord_t   wIndex;
    usbWord_t   wLength;
} usbRequest_t;

int usb_ctrl_cmd (
	libusb_device_handle *dev, 
	int request, 
	int wValue,
	int wIndex, 
	char *buffer, 
	int buflen, 
	int timeout, 
	int in) {
	
	int ret;
	
	if (in) {	
		ret = libusb_control_transfer(dev, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT | LIBUSB_ENDPOINT_IN, request, wValue, wIndex, buffer, buflen, timeout);
	} else {
		ret = libusb_control_transfer(dev, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT | LIBUSB_ENDPOINT_OUT, request, wValue, wIndex, buffer, buflen, timeout);
	}
	if (ret != buflen) {
		printf("usb transmission errors %d\n", ret);
	}
	return ret;
}

static int writeRFPackage(libusb_device_handle *dev, char *package, int pkgLen) 
{
	unsigned char error;
	int ret, i;
	int timeout;
	
	// printf("write RF %d bytes pkg\n", pkgLen);
	
	ret = usb_ctrl_cmd(dev, U2WRQ_RADIO_WRITE, pkgLen, 0, package, pkgLen, 100, FALSE);
	if (ret < 0)
	{
		printf("write RF package error\n");
		return ret;
	}
	
	// get last RF result
	timeout = 100;
	while (-- timeout)
	{
		usleep(1000); // sleep for 1ms
		ret = usb_ctrl_cmd(dev, U2WRQ_LAST_ERR, 0, 0, &error, 1, 100, TRUE);
		if (ret != 1)
		{
			printf("get RF last result error\n");
			return -1;
		}
		
		if (error == RF_WRITE_IDLE)
		{
			continue;
		}
		else
		{
			break;
		}
	} 
	
	if (timeout == 0)
	{
		printf("get RF last result timeout\n");
		return -1;
	}
	
	if (error == RF_WRITE_OK)
	{
		return 0;
	}
	else 
	{
		printf("no response from slave device\n");
		return -1;
	}
}

static int readRFData(libusb_device_handle *dev, char *buffer, int len) 
{
	return usb_ctrl_cmd(dev, U2WRQ_RADIO_READ, len, 0, buffer, len, 100, TRUE);
}

// read number of bytes must be less then 30
int rf_ctrl_cmd(
	libusb_device_handle *dev, 
	int request, 
	int wValue,
	int wIndex, 
	char *buffer, 
	int buflen, 
	int timeout, 
	int in,
	char sn[6]) 
{
	int ret, len, count;
	usbRequest_t req;
	char setup[64];
	
	req.bmRequestType = USBRQ_TYPE_VENDOR;
	req.bRequest = request;
	req.wValue.word = wValue & 0xFFFF;
	req.wIndex.word = wIndex & 0xFFFF;
	req.wLength.word = 8;
	
	memcpy(setup, sn, 6);
	setup[6] = RF_SETUP_SYNC;
	setup[7] = 8;
	memcpy(&setup[8], (char *)&req, sizeof(usbRequest_t));
	
	if (writeRFPackage(dev, setup, 16) != 0)
	{
		return -1;
	}
	
	if(buffer != NULL)
	{		
		if (in)
		{
			memcpy(setup, sn, 6);
			setup[6] = RF_READ_SYNC;
			setup[7] = buflen;
			
			if (writeRFPackage(dev, setup, 8) != 0)
			{
				return -1;
			}
			
			// write an dummy package to get data back
			memcpy(setup, sn, 6);
			
			setup[6] = RF_SETUP_SYNC;
			setup[7] = 8;
			
			req.bmRequestType = USBRQ_TYPE_VENDOR;
			req.bRequest = U2WRQ_IDLE;
			req.wValue.word = 0;
			req.wIndex.word = 0;
			req.wLength.word = 8;
			memcpy(&setup[8], (char *)&req, sizeof(usbRequest_t));
			
			if (writeRFPackage(dev, setup, 16) != 0)
			{
				return -1;
			}
			
			// read data
			usleep(2000);
			return readRFData(dev, buffer, buflen);
		}
		else 
		{
			memcpy(setup, sn, 6);
			count = 0;
			
			while (buflen) 
			{
				if (buflen > 30)
				{
					len = 30;
				}
				else 
				{
					len = buflen;
				}
				
				// printf("write len=%d\n", len);
				buflen -= len;
			
				setup[6] = RF_WRITE_SYNC;
				setup[7] = len;
				
				memcpy(&setup[8], buffer, len);
				buffer += len;
				count += len;
				
				if (writeRFPackage(dev, setup, 8 + len) != 0)
				{
					return -1;
				}
			}
			
			return count;
		}		
	}
	
	return 0;
}

int u2wRFRegRW(libusb_device_handle *dev, int address, int value, int act) {
	int ret;
	int request;
	char buffer[4];
	
	request = U2WRQ_RADIO_REGRW;
	if (act) {
		request |= 0x80;
	}
	
	ret = usb_ctrl_cmd(dev, request, value & 0xFF, address & 0xFF, buffer, 1, 100, TRUE);
	
	if (ret != 1) {
		printf("%s u2w RF-chip reg[0x%x] error\n", act ? "write" : "read", address & 0xFF);
		return -1;
	}
	
	if (act) {
		printf("write 0x%x to u2w RF-chip reg[0x%x]=0x%x\n", value & 0xFF, address & 0xFF, buffer[0] & 0xFF);
	} else {
		printf("u2w RF-chip reg[0x%x]=0x%x\n", address & 0xFF, buffer[0] & 0xFF);
	}
	return buffer[0];
}

int u2wRFRegBit(libusb_device_handle *dev, int address, int index, int act) {
	int ret;
	int request;
	char buffer[4];
	
	request = U2WRQ_RADIO_REGBIT;
	if (act) {
		request |= 0x80;
	}
	
	ret = usb_ctrl_cmd(dev, request, (1 << index) & 0xFF, address & 0xFF, buffer, 1, 100, TRUE);
	
	if (ret != 1) {
		printf("%s u2w RF-chip reg[0x%x] bit%d error\n", act ? "set" : "clear", address & 0xFF, index);
		return -1;
	}
	printf("%s u2w RF-chip reg[0x%x] bit%d, reg[0x%x]=0x%x\n", act?"set":"clear", address & 0xFF, index, 
		address & 0xFF, buffer[0] & 0xFF);
	return buffer[0];
} 

#if (0)
int u2wIO8RW(libusb_device_handle *dev, int address, int value, int act) {
	int ret;
	int request;
	char buffer[4];
	
	request = U2WRQ_LAST_ERR;
	if (act) {
		request |= 0x80;
	}
	
	ret = usb_ctrl_cmd(dev, request, value & 0xFF, address & 0xFF, buffer, 1, 100, TRUE);
	
	if (ret != 1) {
		printf("%s u2w IO8[0x%x] error\n", act ? "write" : "read", address & 0xFF);
		return -1;
	}
	if (act) {
		printf("write 0x%x to u2w IO8[0x%x], IO8[0x%x]=0x%x", value & 0xFF, address & 0xFF, address & 0xFF,
			buffer[0] & 0xFF);
	} else {
		printf("read u2w IO8[0x%x]=0x%x\n", address & 0xFF, buffer[0] & 0xFF);
	}
	return buffer[0];
}
#endif

int u2wMem8RW(libusb_device_handle *dev, int address, int value, int act) {
	int ret;
	int request;
	char buffer[4];
	
	request = U2WRQ_MEM8_RW;
	if (act) {
		request |= 0x80;
	}
	
	ret = usb_ctrl_cmd(dev, request, value & 0xFF, address & 0xFF, buffer, 1, 100, TRUE);
	
	if (ret != 1) {
		printf("%s u2w MEM8[0x%x] error\n", act ? "write" : "read", address & 0xFF);
		return -1;
	}
	if (act) {
		printf("write 0x%x to u2w MEM8[0x%x], MEM8[0x%x]=0x%x", value & 0xFF, address & 0xFF, address & 0xFF,
			buffer[0] & 0xFF);
	} else {
		printf("read u2w MEM8[0x%x]=0x%x\n", address & 0xFF, buffer[0] & 0xFF);
	}
	return buffer[0];
}

#if (0)
int u2wIO8Bit(libusb_device_handle *dev, int address, int pinMask, int act) {
	int ret;
	int request;
	char buffer[4];
	
	request = U2WRQ_IO8_BIT;
	if (act) {
		request |= 0x80;
	}
	
	ret = usb_ctrl_cmd(dev, request, pinMask & 0xFF, address & 0xFF, buffer, 1, 100, TRUE);
	
	if (ret != 1) {
		printf("%s u2w IO8[0x%x] with mask[0x%x] error\n", act ? "set" : "clear", address & 0xFF, pinMask & 0xFF);
		return -1;
	}
	printf("%s u2w IO8[0x%x] with mask[0x%x], IO8[0x%x]=0x%x\n", act ? "set" : "clear", address & 0xFF, pinMask & 0xFF, address & 0xFFFF, buffer[0] & 0xFF);
	return buffer[0];
}
#endif

int u2wMem8Bit(libusb_device_handle *dev, int address, int pinMask, int act) {
	int ret;
	int request;
	char buffer[4];
	
	request = U2WRQ_MEM8_BIT;
	if (act) {
		request |= 0x80;
	}
	
	ret = usb_ctrl_cmd(dev, request, pinMask & 0xFF, address & 0xFF, buffer, 1, 100, TRUE);
	
	if (ret != 1) {
		printf("%s u2w MEM8[0x%x] with mask[0x%x] error\n", act ? "set" : "clear", address & 0xFF, pinMask & 0xFF);
		return -1;
	}
	printf("%s u2w MEM8[0x%x] with mask[0x%x], MEM8[0x%x]=0x%x\n", act ? "set" : "clear", address & 0xFF, pinMask & 0xFF, address & 0xFF, buffer[0] & 0xFF);
	return buffer[0];
}

#if (0)
int u2wSetIO(libusb_device_handle *dev, const char *strPort, int index, int act) {
	int mcuPort;

	if (str2McuGpioAddr(strPort, &mcuPort, NULL, NULL) < 0) {
		return -1;
	}
	// printf("%s PORT[%s] bit %d\n", act?"set":"clear", strPort, index);
	return u2wIO8Bit(dev, mcuPort, (1 << index) & 0xFF, act ? ACT_SET : ACT_CLEAR);
}
#endif

#if (0)
int u2wGetIO(libusb_device_handle *dev, const char *strPort, int index) {
	int mcuPin;
	int buffer;
	
	if (str2McuGpioAddr(strPort, NULL, &mcuPin, NULL) < 0) {
		return -1;
	}	
	
	buffer = u2wIO8RW(dev, mcuPin, 0, ACT_READ);

	if (buffer & (1 << index)) {
		printf("pin is HIGH\n");
	} else {
		printf("pin is LOW\n");
	}
	return 0;
}
#endif

int u2wReset(libusb_device_handle *dev) {
	int ret;
	
	ret = usb_ctrl_cmd(dev, U2WRQ_MCU_RESET, NOT_USED, 0x8579, NULL, NOT_USED, 100, TRUE);
	if (ret != 0) {
		printf("reset u2w device error\n");
		return -1;
	}
	printf("reset u2w device ok\n");
	return 0;
}

#if (0)
int u2wConfigIO(libusb_device_handle *usb_dev, const char *strPort, int index, char *t) {
	int ret, mcuPORT, mcuPIN, mcuDDR;	
	int type;
	
	if (str2McuGpioAddr(strPort, &mcuPORT, &mcuPIN, &mcuDDR) != 0) {
		return -1;
	}
		
	type = str2McuGpioType(t);
	if (type == -1)
		return -1;
	
	ret = 0;
	switch(type) {	
	case GPIO_IPU:
		ret += u2wIO8Bit(usb_dev, mcuDDR, (1 << index), 0);
		ret += u2wIO8Bit(usb_dev, mcuPORT, (1 << index), 1);
		break;
	case GPIO_OUTPUT:
		ret += u2wIO8Bit(usb_dev, mcuDDR, (1 << index), 1);
		break;
	case GPIO_HIZ:
		ret += u2wIO8Bit(usb_dev, mcuDDR, (1 << index), 0);
		ret += u2wIO8Bit(usb_dev, mcuPORT, (1 << index), 0);
		break;
	default:
		return -1;
	}
	return ret ? -1 : 0;
}
#endif





